package com.stone.serverless.core.classloader;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.stone.serverless.sdk.constant.EnvironmentConstant;
import com.stone.serverless.sdk.loader.JarLoadService;
import com.stone.serverless.sdk.loader.ModuleClassLoader;
import lombok.SneakyThrows;

import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @Description: TODO
 * @Author: MECHREVO
 * @Date: 2022/1/13 9:55
 * @Version: 1.0.0
 */
public class JarLoadServiceImpl implements JarLoadService {
    /**
     * 模块化jar的ClassLoader
     */
    Map<String, Map<String, ModuleClassLoader>> jarModuleClassLoaderMap = new LinkedHashMap<>();


    private ModuleClassLoader installLoader(File file) {
        ModuleClassLoader load = new BizJarModuleClassLoader(file, null, this);
        String componentName = load.getComponentName();
        String version = load.getVersion();
        Map<String, ModuleClassLoader> moduleClassLoaderMap = jarModuleClassLoaderMap.getOrDefault(componentName, new HashMap<>());
        moduleClassLoaderMap.put(version, load);
        jarModuleClassLoaderMap.put(componentName, moduleClassLoaderMap);
        return load;
    }

    private List<String> getComponentJars() {
        String property = System.getProperty("user.dir");

        File file = FileUtil.file(property);

        File componentJar = FileUtil.file(file, "component-jar");
        File mkdir = FileUtil.mkdir(componentJar);

        return FileUtil.listFileNames(mkdir.getAbsolutePath());
    }

    private String getComponentJarPath() {
        String property = System.getProperty("user.dir");

        File file = FileUtil.file(property);

        File componentJar = FileUtil.file(file, "component-jar");
        File mkdir = FileUtil.mkdir(componentJar);
        return mkdir.getAbsolutePath();
    }

    private File copyToComponentDir(String filePath) {
        //把这个文件复制到另外一个地方放去
        String componentJarPath = this.getComponentJarPath();

        File tempFile = FileUtil.file(filePath);
        Map<String, Object> environment = this.getEnvironment(tempFile);
        if (ObjectUtil.isNull(environment)) {
            return null;
        }
        Object version = environment.get("version");
        if (ObjectUtil.isNull(version) || CharSequenceUtil.isBlank(version.toString())) {
            version = EnvironmentConstant.DEFAULT_VERSION;
        }
        File copy = FileUtil.copy(tempFile.getAbsolutePath(),
                componentJarPath + "/" + FileUtil.getPrefix(tempFile) + "-" + version + "." + FileUtil.getSuffix(tempFile), true);
        return copy;
    }

    public void refresh() {
        //开始解析外部需要的服务
        for (Map.Entry<String, Map<String, ModuleClassLoader>> entry : jarModuleClassLoaderMap.entrySet()) {
            Map<String, ModuleClassLoader> value = entry.getValue();
            for (Map.Entry<String, ModuleClassLoader> e : value.entrySet()) {
                ModuleClassLoader vv = e.getValue();
                //解析外部需要的服务
                vv.refresh();
            }
        }
    }

    public void initContainer(List<ModuleClassLoader> loaders) {
        //开始解析外部需要的服务
        for (ModuleClassLoader moduleClassLoader : loaders) {
            //解析外部需要的服务
            moduleClassLoader.initContainer();
        }
    }

    public void loaderComponentClasses(List<ModuleClassLoader> loaders) {
        //开始解析外部需要的服务
        for (ModuleClassLoader moduleClassLoader : loaders) {
            //解析外部需要的服务
            moduleClassLoader.loaderComponentClasses();
        }
    }

    public void callRunner(List<ModuleClassLoader> loaders) {
        //开始解析外部需要的服务
        for (ModuleClassLoader moduleClassLoader : loaders) {
            //解析外部需要的服务
            moduleClassLoader.callRunner();
        }
    }

    public void resolveDependencies() {
        //开始解析外部需要的服务
        for (Map.Entry<String, Map<String, ModuleClassLoader>> entry : jarModuleClassLoaderMap.entrySet()) {
            Map<String, ModuleClassLoader> value = entry.getValue();
            for (Map.Entry<String, ModuleClassLoader> e : value.entrySet()) {
                ModuleClassLoader vv = e.getValue();
                //解析外部需要的服务
                vv.resolveDependencies();
            }
        }
    }


    @Override
    public ModuleClassLoader getJarModuleClassLoader(String componentName, String version) {
        Map<String, ModuleClassLoader> moduleClassLoaderMap = jarModuleClassLoaderMap.get(componentName);
        if (MapUtil.isEmpty(moduleClassLoaderMap)) {
            return null;
        }

        ModuleClassLoader moduleClassLoader = moduleClassLoaderMap.get(version);
        return moduleClassLoader;
    }

    /**
     * 是否为jar文件
     *
     * @param file 文件
     * @return 是否为jar文件
     * @since 4.4.2
     */
    private static boolean isJarFile(File file) {
        if (false == FileUtil.isFile(file)) {
            return false;
        }
        return file.getPath().toLowerCase().endsWith(".jar");
    }

    @Override
    public List<ModuleClassLoader> getJarModuleClassLoaders() {
        List<ModuleClassLoader> moduleClassLoaders = new ArrayList<>();
        for (Map.Entry<String, Map<String, ModuleClassLoader>> entry : jarModuleClassLoaderMap.entrySet()) {
            String key = entry.getKey();
            Map<String, ModuleClassLoader> value = entry.getValue();
            for (Map.Entry<String, ModuleClassLoader> e : value.entrySet()) {
                String kk = e.getKey();
                ModuleClassLoader vv = e.getValue();
                moduleClassLoaders.add(vv);
            }
        }

        return moduleClassLoaders;
    }


    @Override
    public void uninstall(String componentName, String version) {
        System.gc();
        ModuleClassLoader jarModuleClassLoader = this.getJarModuleClassLoader(componentName, version);
        if (ObjectUtil.isNotNull(jarModuleClassLoader)) {
            jarModuleClassLoader.uninstall();
        }
        Map<String, ModuleClassLoader> moduleClassLoaderMap = jarModuleClassLoaderMap.get(componentName);
        if (MapUtil.isNotEmpty(moduleClassLoaderMap)) {
            moduleClassLoaderMap.remove(version);
            if (MapUtil.isEmpty(moduleClassLoaderMap)) {
                jarModuleClassLoaderMap.remove(componentName);
            }
        }
        System.gc();
    }


    @SneakyThrows
    @Override
    public void load(String[] jars) {
        System.gc();
        Long startTime = System.currentTimeMillis();
        for (String path : jars) {
            if (!FileUtil.isFile(path)) {
                continue;
            }
            if (!isJarFile(FileUtil.file(path))) {
                Assert.isTrue(true, "必须是jar包");
            }
            this.copyToComponentDir(path);
        }
        Long endTime = System.currentTimeMillis();
        System.out.println("把jar包拷贝到component-jar目录下 耗时" + (endTime - startTime) + "ms");

        List<String> componentJars = this.getComponentJars();
        String componentJarPath = this.getComponentJarPath();

        componentJars = componentJars.stream().map(v -> componentJarPath + FileUtil.FILE_SEPARATOR + v).collect(Collectors.toList());
        install(componentJars);
        System.gc();
    }


    @SneakyThrows
    public void install(List<String> componentJars) {
        Long startTime = System.currentTimeMillis();
        List<CompletableFuture<ModuleClassLoader>> completableFutures = new ArrayList<>();
        List<ModuleClassLoader> loaders = new ArrayList<>();
        for (String path : componentJars) {
            File file = FileUtil.file(path);

            //if (componentJars.size() >= 4) {
            //    CompletableFuture<ModuleClassLoader> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //        ModuleClassLoader moduleClassLoader = installLoader(file);
            //        return moduleClassLoader;
            //    });
            //    completableFutures.add(voidCompletableFuture);
            //} else {
                ModuleClassLoader moduleClassLoader = installLoader(file);
                loaders.add(moduleClassLoader);
           // }
        }
        if (CollUtil.isNotEmpty(completableFutures)) {
            for (CompletableFuture<ModuleClassLoader> completableFuture : completableFutures) {
                ModuleClassLoader moduleClassLoader = completableFuture.get();
                loaders.add(moduleClassLoader);
            }
        }
        Long endTime = System.currentTimeMillis();
        System.out.println("初始化classLoader 耗时" + (endTime - startTime) + "ms");

        startTime = System.currentTimeMillis();
        this.loaderComponentClasses(loaders);
        endTime = System.currentTimeMillis();
        System.out.println("loaderComponentClasses 耗时" + (endTime - startTime) + "ms");


        startTime = System.currentTimeMillis();
        this.initContainer(loaders);
        endTime = System.currentTimeMillis();
        System.out.println("initContainer 耗时" + (endTime - startTime) + "ms");

        startTime = System.currentTimeMillis();
        this.callRunner(loaders);
        endTime = System.currentTimeMillis();
        System.out.println("callRunner 耗时" + (endTime - startTime) + "ms");


        startTime = System.currentTimeMillis();
        this.resolveDependencies();
        endTime = System.currentTimeMillis();
        System.out.println("解析组件外部依赖 耗时" + (endTime - startTime) + "ms");


        startTime = System.currentTimeMillis();
        this.refresh();
        endTime = System.currentTimeMillis();
        System.out.println("刷新容器内实例 耗时" + (endTime - startTime) + "ms");


        startTime = System.currentTimeMillis();
        this.registerRouter(loaders);
        endTime = System.currentTimeMillis();
        System.out.println("注册路由 耗时" + (endTime - startTime) + "ms");


        startTime = System.currentTimeMillis();
        this.publishInstalled();
        endTime = System.currentTimeMillis();
        System.out.println("发布加载成功事件 耗时" + (endTime - startTime) + "ms");

    }


    /**
     * 装载
     *
     * @param jarPath
     */
    @Override
    public void install(String jarPath) {
        System.gc();
        //提前获取环境变量
        boolean b = validRepeatInstall(FileUtil.file(jarPath));
        if (b) {
            return;
        }
        File file = this.copyToComponentDir(jarPath);

        List<String> filePaths = new ArrayList<>();
        filePaths.add(file.getAbsolutePath());
        this.install(filePaths);
        System.gc();
    }

    private Map<String, Object> getEnvironment(File file) {
        InputStream inputStream = ZipUtil.get(file, StandardCharsets.UTF_8, "classes/component.json");
        if (ObjectUtil.isNull(inputStream)) {
            System.out.println("没有描述文件");
            return null;
        }
        String s = IoUtil.readUtf8(inputStream);
        JSONObject jsonObject = JSONUtil.parseObj(s);
        return jsonObject;
    }

    private boolean validRepeatInstall(File file) {

        Map<String, Object> environment = this.getEnvironment(file);
        String componentName = environment.get("componentName").toString();

        Object version = environment.get("version");
        if (ObjectUtil.isNull(version) || CharSequenceUtil.isBlank(version.toString())) {
            version = EnvironmentConstant.DEFAULT_VERSION;
        }
        Map<String, ModuleClassLoader> moduleClassLoaderMap = jarModuleClassLoaderMap.getOrDefault(componentName, new HashMap<>());
        if (moduleClassLoaderMap.containsKey(version)) {
            System.out.println("已经加载过了啊");
            return true;
        }
        return false;
    }

    public void registerRouter(List<ModuleClassLoader> loaders) {
        //开始解析外部需要的服务
        for (ModuleClassLoader moduleClassLoader : loaders) {
            //解析外部需要的服务
            moduleClassLoader.registerRouter();
        }
    }

    @Override
    public List<Map<String, String>> componentInfo() {
        List<Map<String, String>> res = new ArrayList<>();
        for (ModuleClassLoader v : this.getJarModuleClassLoaders()) {
            Map map = new HashMap();
            map.put("name", v.getComponentName());
            map.put("description", v.getDescription());
            map.put("url", v.getUrl());
            map.put("version", v.getVersion());
            res.add(map);
        }
        return res;
    }

    /**
     * 获得所有apiList
     *
     * @param componentName
     * @param version
     * @return
     */
    @Override
    public List<String> routerStatistics(String componentName, String version) {

        ModuleClassLoader jarModuleClassLoader = this.getJarModuleClassLoader(componentName, version);
        if (ObjectUtil.isNull(jarModuleClassLoader)) {
            return null;
        }
        return jarModuleClassLoader.routerStatistics();
    }

    @Override
    public List<Map<String, String>> servicesStatistics(String componentName, String version) {
        ModuleClassLoader jarModuleClassLoader = this.getJarModuleClassLoader(componentName, version);
        if (ObjectUtil.isNull(jarModuleClassLoader)) {
            return null;
        }
        return jarModuleClassLoader.servicesStatistics();

    }

    public void publishInstalled() {
        //开始解析外部需要的服务
        for (Map.Entry<String, Map<String, ModuleClassLoader>> entry : jarModuleClassLoaderMap.entrySet()) {
            Map<String, ModuleClassLoader> value = entry.getValue();
            for (Map.Entry<String, ModuleClassLoader> e : value.entrySet()) {
                ModuleClassLoader vv = e.getValue();
                vv.publishInstalled();
            }
        }
    }
}
